
package com.funo.increment;

import com.scj.testndk.HelloJni;

import org.apache.http.HttpStatus;

import android.app.ActivityManager;
import android.app.Service;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.util.Patterns;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyUtil {
    private static final String TAG = "MyUtil";

    /**
     * 启动更新服务，如果是增量包，那么自动合成文件（.apk），并调用系统安装程序安装。 配置如下权限： <uses-permission
     * android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     * <uses-permission
     * android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     * <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"
     * /> <uses-permission android:name="android.permission.INTERNET" />
     * <uses-permission android:name="android.permission.WAKE_LOCK" />
     * <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
     * /> 下载文件过程会发送广播。 Intent intent = new
     * Intent(MyContant.DOWN_PROCESS_ACTION);
     * intent.putExtra(MyContant.DOWN_PROCESS, process);//百分比进度条
     * intent.putExtra(MyContant.DOWN_PROCESS_TOTAL, all + "M");//这个是总大小
     * intent.putExtra(MyContant.SUCCESS, isSuccess);//如果下载中间出错，这个就是false
     * 
     * @param context
     * @param uri 要下载的http地址。
     * @param packageName 基础包包名。
     * @param wantDownFileName 要下载的文件名。
     * @see #stopDownService(Context)
     */
    public static void startDownService(Context context, String uri, String wantDownFileName) {
        // Intent intent = new Intent(context, DownService.class);
        // intent.putExtra(MyContant.URI_KEY, uri);
        // intent.putExtra(MyContant.PACKAGENAME_KEY, context.getPackageName());
        // intent.putExtra(MyContant.FILENAME_KEY, wantDownFileName);
        // context.startService(intent);
        startDownServiceOther(context, uri, context.getPackageName(), wantDownFileName);
    }

    public static void startDownServiceOther(Context context, String uri, String packageName,
            String wantDownFileName) {
        Intent intent = new Intent(context, DownService.class);
        intent.putExtra(MyContant.URI_KEY, uri);
        intent.putExtra(MyContant.PACKAGENAME_KEY, packageName);
        intent.putExtra(MyContant.FILENAME_KEY, wantDownFileName);
        context.startService(intent);
    }

    /**
     * 停止更新服务。
     * 
     * @param context
     * @see #startDownService(Context, String, String, String)
     */
    public static void stopDownService(Context context) {
        Intent intent = new Intent(context, DownService.class);
        context.stopService(intent);
    }

    /**
     * 通过HTTP协议下载文件。如果是下载APK文件那么就直接返回，如果下载的是patch文件，会自动合成apk包。<br>
     * 注意：配置文件需要配置下面权限,并且下载操作不能放到主线程中。<br>
     * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
     * /> <uses-permission
     * android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     * <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"
     * /> <uses-permission android:name="android.permission.INTERNET" />
     * <uses-permission android:name="android.permission.WAKE_LOCK" />
     * <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
     * /> 下载文件过程会发送广播。 Intent intent = new
     * Intent(MyContant.DOWN_PROCESS_ACTION);
     * intent.putExtra(MyContant.DOWN_PROCESS, process);//百分比进度条
     * intent.putExtra(MyContant.DOWN_PROCESS_TOTAL, all + "M");//这个是总大小
     * intent.putExtra(MyContant.SUCCESS, isSuccess);//如果下载中间出错，这个就是false。
     * 
     * @param uri 要下载的http地址
     * @param packageName 当前包的包名
     * @param wantDownFileName 要下载的文件名称
     * @return 不管是url是下载增量包还是apk包，如果成功返回一个apk包。不成功返回null。
     */
    public static File beginUpdate(Context context, String uri, String packageName,
            String wantDownFileName) {
        File newFile = null;
        try {
            if (context == null) {
                Log.i(TAG, "context是null");
                return null;
            }

            if (TextUtils.isEmpty(uri)) {
                Log.i(TAG, "下载地址是空");
                return null;
            }

            if (TextUtils.isEmpty(wantDownFileName)) {
                Log.i(TAG, "下载文件名是空");
                return null;
            }

            Pattern pattern = Patterns.WEB_URL;
            Matcher m = pattern.matcher(uri);
            if (!m.matches()) {
                Log.i(TAG, "下载地址不是web地址 " + uri);
                return null;
            }

            if (!isNetworkAviable(context)) {
                Log.i(TAG, "网络不可用");
                return null;
            }

            File haveDownFile = downloadFile(context, uri, wantDownFileName);
            if (haveDownFile == null) {
                return null;
            }

            if (haveDownFile.getName().endsWith(".apk")) {
                return haveDownFile;
            }
            // 当前下载的是一个增量包
            if (haveDownFile.getName().endsWith(".patch")) {
                // 拷贝当前旧包
                File currentApk = copyInstallApkToFile(context, packageName, packageName
                        + ".current.apk");
                if (currentApk != null) {
                    HelloJni diff = new HelloJni();
                    String newFilePath_hc = MyContant.PRX + packageName + "_hc.apk";
                    Log.i(TAG, "开始合并包");
                    String result = diff.applyPatch(currentApk.getPath(), newFilePath_hc,
                            haveDownFile.getPath());
                    if (!TextUtils.isEmpty(result) && result.contains(MyContant.SUCCESS)) {
                        Log.i(TAG, "合并包成功");
                        newFile = new File(newFilePath_hc);

                    } else {
                        Log.i(TAG, "合并包失败" + result);
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return newFile;

    }

    /**
     * 安装APK
     * 
     * @param apkPath
     * @param context
     */
    public static void installApk(String apkPath, Context context) {
        if (context == null) {
            Log.i(TAG, "context是null");
            return;
        }
        if (TextUtils.isEmpty(apkPath)) {
            Log.i(TAG, "要按照的apk路径是null");
            return;
        }

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setDataAndType(Uri.fromFile(new File(apkPath)),
                "application/vnd.android.package-archive");
        context.startActivity(intent);
    }

    /**
     * 把已经安装的APK文件拷贝到SD的MyContant.SD_PATH目录下面。
     * 
     * @param context
     * @param packageName 要拷贝的APK包名（这个包要是已经安装的）。
     * @param newApkFileName sd下面要保存文件的文件名,比如test.apk。
     * @return 返回拷贝成功的文件，如果不成功那么就是null。
     */
    public static File copyInstallApkToFile(Context context, String packageName,
            String newApkFileName) {

        if (context == null) {
            Log.i(TAG, "context 是 null ");
            return null;
        }

        String localApkPath = MyUtil.getAPkPathByPackageName(context, packageName);
        if (TextUtils.isEmpty(localApkPath)) {
            Log.i(TAG, "获取本地已经安装apk文件路径失败，请检查包名或者是否已经安装该包 " + packageName);
            return null;
        }

        File file = MyUtil.copyInstallApkToFile(localApkPath, newApkFileName);

        return file;
    }

    /**
     * 根据包名获取当前包的路径。
     * 
     * @param packageName
     * @return 如果不成功就返回null。
     */
    public static String getAPkPathByPackageName(Context context, String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return null;
        }

        String result = null;

        try {
            PackageManager packageManager = context.getPackageManager();
            List<ApplicationInfo> installedAppList = packageManager
                    .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
            for (ApplicationInfo appInfo : installedAppList) {
                if (appInfo.packageName.equals(packageName)) {
                    result = appInfo.publicSourceDir;
                    Log.i(TAG, "成功获取APK路径:" + result);
                    break;
                }
            }
            installedAppList.clear();
            installedAppList = null;
            packageManager = null;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 把已经安装的APK文件拷贝到SD的MyContant.SD_PATH目录下面。
     * 
     * @param sourceFilePath 要拷贝的APK文件路径。
     * @param newApkFileName sd下面要保存文件的文件名，必须以.apk结尾。
     * @return 返回拷贝成功的文件，如果不成功那么就是null。
     */
    public static File copyInstallApkToFile(String sourceFilePath, String newApkFileName) {

        if (TextUtils.isEmpty(sourceFilePath)) {
            Log.i(TAG, "要拷贝的源文件是空");
            return null;
        }
        if (TextUtils.isEmpty(newApkFileName)) {
            Log.i(TAG, "新文件是空");
            return null;
        }

        if (!newApkFileName.endsWith(".apk")) {
            Log.i(TAG, "新文件名称必须以 .apk结尾" + newApkFileName);
            return null;
        }

        InputStream inStream = null;
        FileOutputStream fs = null;
        File file = null;
        File inputFile = null;
        try {
            int byteread = 0;
            inputFile = new File(sourceFilePath);

            if (!inputFile.exists()) {
                Log.i(TAG, "要拷贝的文件不存在:" + sourceFilePath);
                return null;
            }

            inStream = new FileInputStream(inputFile);

            File fileDic = new File(MyContant.PRX + newApkFileName);
            if (!fileDic.exists()) {
                fileDic.mkdirs();
            }
            file = new File(MyContant.PRX + newApkFileName);
            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();// 如果不存在该路径，那么就创建
            fs = new FileOutputStream(file);
            byte[] buffer = new byte[1024];
            while ((byteread = inStream.read(buffer)) != -1) {
                fs.write(buffer, 0, byteread);
            }
            inStream.close();
            fs.close();
            Log.i(TAG, "拷贝文件成功:" + file.getPath());
        } catch (Exception e) {
            e.printStackTrace();
            file = null;
        } finally {

            try {
                if (inStream != null)
                    inStream.close();
                if (fs != null) {
                    fs.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return file;
    }

    /**
     * 获取本地版本信息。 String[].length为零，则取本地版本信息出现异常
     * 
     * @param context
     * @return {version_code,version_name} 例如{"10","version 3.2.19"}。
     */
    public static String[] getLocalVersion(Context context) {
        PackageManager packageManager = context.getPackageManager();
        try {
            PackageInfo info = packageManager.getPackageInfo(context.getPackageName(), 0);
            String version_code = String.valueOf(info.versionCode);// 代码版本
            String version_name = info.versionName; // 版本名
            return new String[] {
                    version_code, version_name
            };
        } catch (NameNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 通过HTTP协议下载文件。
     * 
     * @param context
     * @param remoteUrl
     * @param wantDownFileName 这个必须有后缀名
     * @return 不成功返回null。
     */
    public static File downloadFile(Context context, String remoteUrl, String wantDownFileName) {

        if (TextUtils.isEmpty(remoteUrl)) {
            return null;
        }

        // 先判断当前要下载下来的文件夹是否存在
        File fileDic = new File(MyContant.PRX + wantDownFileName);
        if (!fileDic.exists()) {
            fileDic.mkdirs();
        }
        // 如果已经存在该文件，把当前文件删除
        File savefile = new File(MyContant.PRX + wantDownFileName);
        if (savefile.exists()) {
            savefile.delete();
        }

        boolean isApk = wantDownFileName.toLowerCase().endsWith("apk");
        InputStream is = null;

        // 把从服务器读出的下载流写入到本地临时文件
        FileOutputStream fos = null;
        BufferedInputStream bis = null;
        try {
            Log.i(TAG, "开始下载新版本:" + remoteUrl);
            // 创建一个新的文件
            savefile.createNewFile();
            URL url = new URL(remoteUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            int total = conn.getContentLength();
            conn.setConnectTimeout(5000);
            if (conn.getResponseCode() != HttpStatus.SC_OK) {
                return null;
            }
            is = conn.getInputStream();

            fos = new FileOutputStream(savefile);

            bis = new BufferedInputStream(is);
            byte[] buffer = new byte[1024 * 1024];
            int len;
            int haveDown = 0;
            sendDowning(context, "0%", total, true, isApk);
            int process = 0;
            while ((len = bis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
                haveDown = haveDown + len;
                process = (haveDown * 100 / total);
                if (process > 0) {
                    sendDowning(context, process + "%", total, true, isApk);
                }
            }
            Log.i(TAG, "下载新版本完成");
            return savefile;
        } catch (Exception e) {
            e.printStackTrace();
            sendDowning(context, "0%", 0, false, isApk);
            return null;
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (bis != null) {
                    bis.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                if (is != null) {
                    is.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 发生下载进度广播.
     * 
     * @param context
     * @param process 百分比
     * @param total 总大小 字节
     */
    public static void sendDowning(Context context, String process, int total, boolean isSuccess,
            boolean isApk) {
        double all = Double.valueOf((total * 100 / (1024 * 1024))) / 100;
        Log.i(TAG, "下载进度 " + process + " 总大小是 " + all + "M");
        Intent intent = new Intent(MyContant.DOWN_PROCESS_ACTION);
        intent.putExtra(MyContant.DOWN_PROCESS, process);
        intent.putExtra(MyContant.DOWN_PROCESS_TOTAL, all + "M");
        intent.putExtra(MyContant.SUCCESS, isSuccess);
        intent.putExtra(MyContant.IS_APK, isApk);
        context.sendBroadcast(intent);

    }

    /**
     * 判断当前网络是否可用。
     * 
     * @param context
     * @return 网络是否可以使用。
     * @see #getNetWorkType(Context)
     */
    public static boolean isNetworkAviable(Context context) {
        if (context == null) {
            return false;
        }
        try {
            ConnectivityManager connectionManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);

            NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
            return (networkInfo != null && networkInfo.isConnected());
        } catch (Exception e) {
            if (e != null) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 返回当前APK所有在运行的任务。
     * 
     * @param context
     * @return
     */
    public static ServiceInfo[] getAllService(Context context) {
        ServiceInfo[] ourServices = null;
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo pi = null;
            try {
                pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SERVICES);
            } catch (NameNotFoundException e) {
                Log.i("IncrementUtil", e.getMessage());
            }
            if (pi != null) {
                ourServices = pi.services;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ourServices;
    }

    /**
     * 用来判断当前服务是否已经在运行。
     * 
     * @param context
     * @param serviceName
     * @return
     */
    public static boolean isCurrentServiceRun(Context context) {
        boolean result = false;
        try {
            ServiceInfo[] ourServices = getAllService(context);
            if (ourServices == null || ourServices.length == 0) {
                return result;
            }
            for (int i = 0; i < ourServices.length; i++) {
                ServiceInfo tempSInfo = ourServices[i];
                if (tempSInfo.name.equalsIgnoreCase(DownService.class.getName())) {
                    if (serviceIsWorked(context, DownService.class.getName())) {
                        result = true;
                        break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;

    }

    /**
     * 判断其他服务是否启动
     * 
     * @param context
     * @param serviceName like
     *            com.funo.networktesttassistant.service.NetTestService
     * @return service是否启用。
     */
    public static boolean serviceIsWorked(Context context, String serviceName) {
        ActivityManager myManager = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);
        ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) myManager
                .getRunningServices(Integer.MAX_VALUE);
        for (int i = 0; i < runningService.size(); i++) {
            String name = runningService.get(i).service.getClassName().toString();
            if (name.equalsIgnoreCase(serviceName)) {
                return true;
            }
        }
        return false;
    }

}
